#include <QTextStream>
#include <QFile>
#include <QTextCodec>
#include <QDebug>
#include <QThread>
#include <QRegExp>
#include <QStringList>
#include "rconstdifinition.h"
#include "rfilemodel.h"
#include "rengine.h"

const qint64 CStreamTestLength = 200;
const QByteArray CLineRoleName = "textLine";

RFileModel::RFileModel(REngine& engine, QObject *parent) :
    QAbstractListModel(parent), m_engine(engine),
    m_codec(NULL), m_file(NULL)
{
    QHash<int, QByteArray> roles;
    roles[0] = CLineRoleName;
    setRoleNames(roles);
}

RFileModel::~RFileModel() {
    if (m_file) {
        m_file->close();
    }
    delete m_file;
}

bool RFileModel::setFile(const QString& fileName) {
    bool ret = false;
    beginResetModel();
    if (openFile(fileName)) {
        if (readFileLinePosition() &&
                m_file_line_pos.count()) {
            qDebug()<<"using existing file line position";
            ret = true;
        }
        else {
            if (analyzeFile()) {
                qDebug()<<"creating new file line position";
                ret = saveFileLinePosition();
            }
        }
        initFileInfo();
        m_file_id = m_engine.fileId(fileName);
    }
    endResetModel();
    return ret;
}

bool RFileModel::initFileInfo() {
    //Do nothing
    return true;
}

void RFileModel::reset() {
    beginResetModel();
    m_file->close();
    setFile(m_file->fileName());
    endResetModel();
}

int RFileModel::rowCount(const QModelIndex & /*parent*/) const {
    return m_file_line_pos.count();
}

QVariant RFileModel::data(const QModelIndex & index, int /*role*/) const {
    int row = index.row();
    return data(row);
}

QString RFileModel::fileName() {
    if (m_file) {
        return m_file->fileName();
    }
    else {
        return QString();
    }
}

int RFileModel::fileId() {
    return m_file_id;
}

void RFileModel::setFileId(int fileId) {
    m_file_id = fileId;
}

int RFileModel::newLinePosPadding() {
    int padding = 0;
    if (m_codec->name() == KCodecUTF16LE) {
        padding = 1;
    }
    else if (m_codec->name() == KCodecUTF32LE) {
        padding = 3;
    }
    else {
        padding = 0;
    }
    return padding;
}

int RFileModel::newLineCode(const QTextCodec& codec) {
    //TODO add codec that newline char is not 0x0A here
    QString name(codec.name());
    if (name == KCodecUTF16LE) {
        return KNewLineCodeUtf16Le;
    }
    else if (name == KCodecUTF16BE) {
        return KNewLineCodeUtf16Be;
    }
    else if (name == KCodecUTF32LE) {
        return KNewLineCodeUtf32Le;
    }
    else if (name == KCodecUTF32BE) {
        return KNewLineCodeUtf32Be;
    }
    else {
        return KNewLineCodeDefault;
    }
}

bool RFileModel::newLineCharIsLittleEndian(const QTextCodec& codec) {
    QString name(codec.name());
    if (name == KCodecUTF16LE) {
        return true;
    }
    else if (name == KCodecUTF16BE) {
        return false;
    }
    else if (name == KCodecUTF32LE) {
        return true;
    }
    else if (name == KCodecUTF32BE) {
        return false;
    }
    else {
        return true;
    }
}

bool RFileModel::lastCharIsNewLine(const QByteArray & array, const QTextCodec & codec) {
    int fetchMore;
    int newLineCharLength = newLineCharLaw(codec, fetchMore);
//    int newLine = 0;
    if (array.length() >= newLineCharLength) {
//        while (newLineCharLength > 0) {
//            newLine |= array[array.length()-newLineCharLength]<<(8*(newLineCharLength-1));
//            newLineCharLength--;
//        }
        if (m_engine.byteArrayToUint(array.right(newLineCharLength),
                                     newLineCharIsLittleEndian(codec)) == KNewLineCodeDefault) {
//            qDebug()<<"Find new line";
            return true;
        }
        else {
            return false;
        }
    }
    else {
        return false;
    }
}

int RFileModel::newLineCharLaw(const QTextCodec& codec, int& fetchMore) {
    int length = 1;
    QString name(codec.name());
    //TODO add codec that newline length is not 1 here
    if (name == KCodecUTF16LE) {
        length = 2;
        fetchMore = 1;
    }
    else if ( name == KCodecUTF16BE) {
        length = 2;
        fetchMore = -1;
    }
    else if (name == KCodecUTF32LE) {
        length = 4;
        fetchMore = 3;
    }
    else if (name == KCodecUTF32BE) {
        length = 4;
        fetchMore = -3;
    }
    else {
        length = 1;
        fetchMore = 0;
    }
    return length;
}

int RFileModel::lineNumberOfPercent(float percent) {
    if (percent <0 || percent > 1) {
        qDebug()<<"ERROR::percent is invalid:"<<percent;
        return 0;
    }
    return m_file_line_pos.count()*percent;
}

float RFileModel::lineNumberToPercent(int lineNum) {
    if (lineNum <0 || lineNum > m_file_line_pos.count()) {
        qDebug()<<"ERROR::lineNum is invalid:"<<lineNum;
        return 0;
    }
    return ((float)lineNum)/m_file_line_pos.count();
}

QString RFileModel::fileCodec() {
    if (m_codec) {
        return m_codec->name();
    }
    else {
        return KCodecNotAvailable;
    }
}
int RFileModel::openFileResult() {
    return m_open_result;
}

void RFileModel::setOpenFileResult(int result) {
    m_open_result = result;
}
